package com.joysuch.wwyt.edu.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Lists;
import com.joysuch.wwyt.alert.service.AlertMessageService;
import com.joysuch.wwyt.bp.constant.AppNotifyContentPre;
import com.joysuch.wwyt.common.entity.CommonBusinessNotice;
import com.joysuch.wwyt.common.enums.AppBusinessNoticeTypes;
import com.joysuch.wwyt.common.service.CommonBusinessNoticeService;
import com.joysuch.wwyt.common.service.CommonIndexNotifyService;
import com.joysuch.wwyt.config.RedisClient;
import com.joysuch.wwyt.core.common.util.SimpleDateFormatCache;
import com.joysuch.wwyt.core.entity.BaseConfig;
import com.joysuch.wwyt.core.enums.BaseConfigCodes;
import com.joysuch.wwyt.core.enums.Status;
import com.joysuch.wwyt.core.exception.ApiBusinessException;
import com.joysuch.wwyt.core.repository.BaseUserDao;
import com.joysuch.wwyt.core.service.BaseConfigService;
import com.joysuch.wwyt.core.support.Context;
import com.joysuch.wwyt.edu.bean.CommonBean;
import com.joysuch.wwyt.edu.bean.EduRandomSelectQuestionConfig;
import com.joysuch.wwyt.edu.entity.*;
import com.joysuch.wwyt.edu.enums.ExamJoinInTargetTypes;
import com.joysuch.wwyt.edu.enums.TrainingFrom;
import com.joysuch.wwyt.edu.enums.TrainingPublishStatus;
import com.joysuch.wwyt.edu.mapper.EduExamMapper;
import com.joysuch.wwyt.edu.mapper.EduTrainningRecordUserMapper;
import com.joysuch.wwyt.edu.repository.EduExamTestPaperDao;
import com.joysuch.wwyt.edu.repository.EduExamUserScopeDao;
import com.joysuch.wwyt.edu.repository.EduTestPaperQuestionDao;
import com.joysuch.wwyt.edu.service.EduAsyncService;
import com.joysuch.wwyt.edu.service.EduQuestionService;
import com.joysuch.wwyt.edu.service.EduTestPaperQuestionService;
import com.joysuch.wwyt.edu.service.EduTestPaperService;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RKeys;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.text.MessageFormat;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class EduAsyncServiceImpl implements EduAsyncService {
    @Autowired
    private BaseConfigService baseConfigService;
    @Autowired
    private EduExamMapper eduExamMapper;
    @Autowired
    private AlertMessageService alertMessageService;
    @Autowired
    private CommonIndexNotifyService commonIndexNotifyService;
    @Autowired
    private CommonBusinessNoticeService commonBusinessNoticeService;
    @Autowired
    private BaseUserDao baseUserDao;
    @Autowired
    private EduExamUserScopeDao eduExamUserScopeDao;
    @Autowired
    private EduTrainningRecordUserMapper eduTrainningRecordUserMapper;
    @Autowired
    private EduExamTestPaperDao eduExamTestPaperDao;
    @Autowired
    private EduTestPaperService testPaperService;
    @Autowired
    private EduTestPaperQuestionService eduTestPaperQuestionService;
    @Autowired
    private EduQuestionService questionService;
    @Autowired
    private EduTestPaperQuestionDao eduTestPaperQuestionDao;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RedisClient redisClient;
    @Override
    @Async("formDataStatisticAsync")
    public void saveToTriggerTraining(EduTrainningRecord eduTrainningRecord, Long id, CommonBean commonBean) {
        Set<Long> users = getTrainingUsersIds(id);
        BaseConfig trainningConfig = baseConfigService.findFirstConfig(BaseConfigCodes.GETUI_TRAINNING_NOTIFICATION_CONTENT);
        BaseConfig examConfig = baseConfigService.findFirstConfig(BaseConfigCodes.GETUI_EXAM_PUBLISH_NOTIFICATION_CONTENT);
        BaseConfig notificationTitle = baseConfigService.findFirstConfig(BaseConfigCodes.GETUI_EXAM_NOTIFICATION_TITLE);
        BaseConfig cancelConfig = baseConfigService.findFirstConfig(BaseConfigCodes.GETUI_EXAM_CANCEL_NOTIFICATION_CONTENT);
        BaseConfig teacherConfig = baseConfigService.findFirstConfig(BaseConfigCodes.GETUI_TRAINNING_NOTIFICATION_TEACHER_CONTENT);

        if (notificationTitle == null ||trainningConfig ==null ||examConfig==null ||cancelConfig==null ||teacherConfig==null ) {
            throw new RuntimeException(Status.NOTICE_NOT_CONFIGURATION_PARAM.getMsg());
        }

        String trainningMsgBody = String.format(trainningConfig.getValue(), TrainingFrom.findNameById(eduTrainningRecord.getTrainingForm()),eduTrainningRecord.getTrainingName(), SimpleDateFormatCache.getYmdhm().format(eduTrainningRecord.getTrainningDate()));
        EduExam eduExam = eduExamMapper.selectById(eduTrainningRecord.getExamId());
        String examMsgBody="";
        if (!ObjectUtils.isEmpty(eduExam)){
            examMsgBody = String.format(examConfig.getValue(), TrainingFrom.findNameById(Math.toIntExact(eduExam.getFormType())),eduExam.getName(),SimpleDateFormatCache.getYmdhm().format(eduTrainningRecord.getTrainningDate()));
        }
        String msgTitle = notificationTitle.getValue();
        String cancelMsg = cancelConfig.getValue();
        String teacherMsg = teacherConfig.getValue();

        Set<Long> teacherId=new HashSet<>(1);
        if (!ObjectUtils.isEmpty(eduTrainningRecord.getTrainningTeacherId())){
            teacherId.add(eduTrainningRecord.getTrainningTeacherId());
        }

        //发布状态
        if (eduTrainningRecord.getTrainingPublishStatus()== TrainingPublishStatus.PUBLISH.getId()){
            alertMessageService.pushSingleBatchCid(users, msgTitle, trainningMsgBody, null, true);
            //向web端发送培训发布通知
            commonIndexNotifyService.releaseExamReminder(users, eduTrainningRecord.getId(), msgTitle, eduTrainningRecord.getTrainningDate(), trainningMsgBody,commonBean);
            //发布培训通知
            generateAppNotifyTraining(users,trainningMsgBody,eduTrainningRecord.getId(),commonBean);
            if (eduTrainningRecord.getExamId()!=null){
                alertMessageService.pushSingleBatchCid(users, msgTitle, examMsgBody, null, true);
                //向web端发送考试发布通知
                commonIndexNotifyService.releaseExamReminder(users, eduTrainningRecord.getExamId(), msgTitle, eduTrainningRecord.getTrainningDate(), examMsgBody,commonBean);
                //如果关联考试再发布考试通知
                generateAppExamNotify(users,examMsgBody,eduTrainningRecord.getExamId(),commonBean);
            }
            if (eduTrainningRecord.getTrainingForm()==TrainingFrom.ONLINE.getId()){
                //线下通知培训人员和讲师
                String teacher = String.format(teacherMsg, eduTrainningRecord.getTrainingName(), SimpleDateFormatCache.getYmdhm().format(eduTrainningRecord.getTrainningDate()), eduTrainningRecord.getTrainningPlace());
                if (eduTrainningRecord.getTrainingPublishStatus()== TrainingPublishStatus.PUBLISH.getId()){
                    alertMessageService.pushSingleBatchCid(teacherId, msgTitle, teacher, null, true);
                    //向web端发送培训发布通知
                    commonIndexNotifyService.releaseExamReminder(teacherId, eduTrainningRecord.getId(), msgTitle, eduTrainningRecord.getTrainningDate(), teacher,commonBean);
                    //发布培训通知
                    generateAppNotifyTraining(teacherId,teacher,eduTrainningRecord.getId(),commonBean);
                }
            }
        }else {
            users.addAll(teacherId);
            //取消通知
            String cancelTrainning = String.format(cancelMsg, eduTrainningRecord.getTrainingName());
            alertMessageService.pushSingleBatchCid(users, msgTitle, cancelTrainning, null, true);
            //向web端发送培训取消通知
            commonIndexNotifyService.releaseExamReminder(users, eduTrainningRecord.getId(), msgTitle, eduTrainningRecord.getTrainningDate(), cancelTrainning,commonBean);
            generateAppNotifyTraining(users,cancelTrainning,eduTrainningRecord.getId(),commonBean);
            if (eduTrainningRecord.getExamId()!=null) {
                //如果关联考试再发布取消考试通知
                String cancelExam = String.format(cancelMsg, eduExam.getName());
                alertMessageService.pushSingleBatchCid(users, msgTitle, cancelExam, null, true);
                commonIndexNotifyService.releaseExamReminder(users, eduTrainningRecord.getId(), msgTitle, eduTrainningRecord.getTrainningDate(), cancelExam,commonBean);
                generateAppExamNotify(users,cancelExam,eduTrainningRecord.getExamId(),commonBean);
            }
        }
    }


    @Override
    @Async("formDataStatisticAsync")
    public void saveToTriggerExam(Long examId, Date startTime, String code, Integer state,CommonBean commonBean) {
        Set<Long> userIds = getUsersIds(examId);
        BaseConfig baseConfig = new BaseConfig();
        EduExam eduExam = eduExamMapper.selectById(examId);
        BaseConfig notificationTitle = baseConfigService.findFirstConfig(BaseConfigCodes.GETUI_EXAM_NOTIFICATION_TITLE);
        if (notificationTitle == null) {
            throw new RuntimeException(Status.NOTICE_NOT_CONFIGURATION_PARAM.getMsg());
        }
        String msgTitle = notificationTitle.getValue();
        if (state == 1){
            baseConfig = baseConfigService.findFirstConfig(BaseConfigCodes.GETUI_EXAM_PUBLISH_NOTIFICATION_CONTENT);
            String subMsgBody = baseConfig.getValue();
            String msgBody = String.format(subMsgBody, TrainingFrom.findNameById(Math.toIntExact(eduExam.getFormType())),eduExam.getName(),SimpleDateFormatCache.getYmdhm().format(eduExam.getStartTime()));
            alertMessageService.pushSingleBatchCid(userIds, msgTitle, msgBody, null, true);
            //向web端发送通知
            commonIndexNotifyService.releaseExamReminder(userIds, examId, msgTitle, startTime, msgBody,commonBean);
            // app通知
            generateAppNotifyExam(userIds, msgBody, examId,commonBean);
        }else {
            baseConfig = baseConfigService.findFirstConfig(BaseConfigCodes.GETUI_EXAM_CANCEL_NOTIFICATION_CONTENT);
            String subMsgBody = baseConfig.getValue();
            String msgBody = String.format(subMsgBody, eduExam.getName());
            alertMessageService.pushSingleBatchCid(userIds, msgTitle, msgBody, null, true);
            //向web端发送通知
            commonIndexNotifyService.releaseExamReminder(userIds, examId, msgTitle, startTime, msgBody,commonBean);
            // app通知
            generateAppNotifyExam(userIds, msgBody, examId,commonBean);
        }
        if (baseConfig == null) {
            throw new RuntimeException(Status.NOTICE_NOT_CONFIGURATION_PARAM.getMsg());
        }
    }

    @Override
    @Async("formDataStatisticAsync")
    public void saveRelatedTestPapers(Long id, List<Long> testPapers, List<Long> userIds,CommonBean commonBean) {
        eduTestPaperQuestionDao.deleteByExamId(id);
        if (testPapers == null || testPapers.isEmpty()) {
            return;
        }
//        if (isExam){
//            List<EduExamTestPaper> list = testPapers.stream().map(testPaperId ->
//                            {
//                                EduExamTestPaper eduExamTestPaper = new EduExamTestPaper();
//                                eduExamTestPaper.setExamId(id);
//                                eduExamTestPaper.setTestPaperId(testPaperId);
//                                eduExamTestPaper.setIndexNo(0);
//                                eduExamTestPaper.setOrgCode(commonBean.getOrgCode());
//                                eduExamTestPaper.setTenentId(commonBean.getTenantId());
//                                return eduExamTestPaper;
//                            }
//                    ).collect(Collectors.toList());
//            eduExamTestPaperDao.saveAll(list);
//        }
        EduTestPaper paper = testPaperService.findById(testPapers.get(0));
        //如果是随机选题 每个人看到的不一样
        if (StringUtils.isNotBlank(paper.getCreateMode()) && "random".equals(paper.getCreateMode())) {
            if (org.apache.commons.collections.CollectionUtils.isNotEmpty(userIds)) {
                List<EduTestPaperQuestion> result = Lists.newArrayList();
                for (Long userId : userIds) {
                    List<EduRandomSelectQuestionConfig> configs = JSONArray.parseArray(paper.getCreateConfig(), EduRandomSelectQuestionConfig.class);
                    List<EduQuestionServiceImpl.QuestionSelectWrapper> wrappers = questionService.randomSelectWrapper(configs);
                    List<EduTestPaperQuestion> questions = saveQuestions(paper.getId(), wrappers, userId, id,commonBean);
                    result.addAll(questions);
                }
//                eduTestPaperQuestionDao.saveAll(result);
                eduTestPaperQuestionService.saveBatch(result);
            }

        }
    }

    @Override
    @Async("investigateTaskTimeCalculate")
    public void deleteRedisKeyByExamId(Long id) {
        String redisKey = "CACHE_ANSWERS:"+id+":*";
        String redisKeyMinutees = "CACHETES_MINUTES:" + id+":*";
        String redisKeyEndTime = "CACHETES_SECONDS:" + id+":*";
        String redisSwers = "ANSWERS_CACHE:" + "swers" + ":*" ;
        String redisMinutes = "ANSWERS_CACHE:" + "minutes" + ":*" ;
        String redisTime = "CACHE_EXAM_TIME:"+id+":*" ;

        List<Long> userIds = eduExamMapper.selectUserByExamId(id);
        List<String> userKeys=new ArrayList<>();
        List<String> minutes=new ArrayList<>();
        if (!CollectionUtil.isEmpty(userIds)){
            minutes= userIds.stream().map(userId -> "ANSWERS_CACHE:" + "minutes" + ":"+userId).collect(Collectors.toList());
            userKeys=userIds.stream().map(userId -> "ANSWERS_CACHE:" + "swers" + ":"+userId).collect(Collectors.toList());
        }

        RKeys rKeys = redissonClient.getKeys();
        Iterable<String> keys = rKeys.findKeysByPattern(redisKey);
        keys.forEach(s -> redisClient.removeKey(s));
        Iterable<String> min = rKeys.findKeysByPattern(redisKeyMinutees);
        min.forEach(s -> redisClient.removeKey(s));
        Iterable<String> end = rKeys.findKeysByPattern(redisKeyEndTime);
        end.forEach(s -> redisClient.removeKey(s));
        Iterable<String> times = rKeys.findKeysByPattern(redisTime);
        times.forEach(s -> redisClient.removeKey(s));
        Iterable<String> swers = rKeys.findKeysByPattern(redisSwers);

        List<String> finalUserKeys = userKeys;
        swers.forEach(key -> {
            if (!CollectionUtil.isEmpty(finalUserKeys)){
                finalUserKeys.forEach(s -> {
                    if (key.equals(s)){
                        redisClient.removeKey(s);
                    }
                });
            }
        });

        Iterable<String> redisMinutesKey = rKeys.findKeysByPattern(redisMinutes);
        List<String> finalMinutes = minutes;
        redisMinutesKey.forEach(key -> {
            if (!CollectionUtil.isEmpty(finalMinutes)){
                finalMinutes.forEach(s -> {
                    if (key.equals(s)){
                        redisClient.removeKey(s);
                    }
                });
            }
        });

    }

    private List<EduTestPaperQuestion> saveQuestions(Long id, List<EduQuestionServiceImpl.QuestionSelectWrapper> questions, Long userId, Long examId,CommonBean commonBean) {
        if (questions == null || questions.isEmpty()) {
            return Lists.newArrayList();
        }
        Long tenantId = commonBean.getTenantId();
        String orgCode = commonBean.getOrgCode();
        Long currentUserId = commonBean.getCurrentUserId();
        List<EduTestPaperQuestion> list = new ArrayList<>(questions.size());
        int index = 0;
        // 如下修改逻辑允许同一题出现多次，如果不允许出现多次，需要按照questionid去重
        for (EduQuestionServiceImpl.QuestionSelectWrapper q : questions) {
            EduTestPaperQuestion question = new EduTestPaperQuestion();
            question.setQuestionId(q.q.getId());
            question.setScore(q.config.getScore().doubleValue());
            question.setTestPaperId(id);
            question.setUserId(userId);
            question.setExamId(examId);
            question.setIndexNo(index++);
            question.setTenentId(tenantId);
            question.setOrgCode(orgCode);
            question.setCreateBy(currentUserId);
            question.setCreateTime(new Date());
            question.setUpdateBy(currentUserId);
            question.setUpdateTime(new Date());
            question.setDeleteFlag("0");
            list.add(question);
        }
        return list;
//        eduTestPaperQuestionDao.saveAll(list);
    }

    private Set<Long> getTrainingUsersIds(Long id) {
        List<EduTrainningRecordUser> recordUsers = eduTrainningRecordUserMapper
                .selectList(new LambdaQueryWrapper<EduTrainningRecordUser>()
                        .eq(EduTrainningRecordUser::getTrainningRecordId, id)
                        .eq(EduTrainningRecordUser::getDeleteFlag,"0"));
        Set<Long> set = new HashSet<>();
        if (!CollectionUtil.isEmpty(recordUsers)){
            for (EduTrainningRecordUser recordUser : recordUsers) {
                set.add(recordUser.getUserId());
            }
        }
        return set;
    }

    public Set<Long> getUsersIds(Long examId) {
        Set<Long> userIds = new HashSet<>();
        List<EduExamUserScope> scopes = eduExamUserScopeDao.findByExamId(examId);
        for (EduExamUserScope scope : scopes) {
            if (ExamJoinInTargetTypes.USER.getCode().equals(scope.getObjType())) {
                userIds.add(scope.getObjId());
                continue;
            }
            if (ExamJoinInTargetTypes.JOB.getCode().equals(scope.getObjType())) {
                List<Long> ids = baseUserDao.findIdByJobAndCompany(scope.getObjId());
                userIds.addAll(ids);
                continue;
            }
            if (ExamJoinInTargetTypes.DEPART.getCode().equals(scope.getObjType())) {
                List<Long> ids = baseUserDao.findByDepartIdAndCompany(scope.getObjId());
                userIds.addAll(ids);
            }
        }
        return userIds;
    }

    private void generateAppExamNotify(Set<Long> users, String examMsgBody, Long examId,CommonBean commonBean) {
        ArrayList<CommonBusinessNotice> commonBusinessNotices = new ArrayList<>();
        for (Long userId : users) {
            String msgBody = MessageFormat.format(examMsgBody, baseUserDao.getRealNameById(userId));
            CommonBusinessNotice appNotify = new CommonBusinessNotice();
            appNotify.setBusinessType(AppBusinessNoticeTypes.EDU.getType())
                    .setNoticeContent(msgBody)
                    .setNoticeUser(userId)
                    .setNoticeTitle(AppNotifyContentPre.EDU_NOTIFY)
                    .setSendTime(new Date())
                    .setRefId(examId);
            appNotify.setOrgCode(commonBean.getOrgCode());
            appNotify.setTenentId(commonBean.getTenantId());
            commonBusinessNotices.add(appNotify);
        }
        commonBusinessNoticeService.saveBatch(commonBusinessNotices);
    }
    private void generateAppNotifyExam(Set<Long> userIds, String msgBody, Long examId,CommonBean commonBean) {
        // app通知中心
        ArrayList<CommonBusinessNotice> commonBusinessNotices = new ArrayList<>();
        for (Long userId : userIds) {
            String msg = MessageFormat.format(msgBody, baseUserDao.getRealNameById(userId));
            CommonBusinessNotice appNotify = new CommonBusinessNotice();
            appNotify.setBusinessType(AppBusinessNoticeTypes.EDU.getType())
                    .setNoticeContent(msg)
                    .setNoticeUser(userId)
                    .setNoticeTitle(AppNotifyContentPre.EDU_NOTIFY)
                    .setSendTime(new Date())
                    .setRefId(examId);
            appNotify.setOrgCode(commonBean.getOrgCode());
            appNotify.setTenentId(commonBean.getTenantId());
            commonBusinessNotices.add(appNotify);
        }
        commonBusinessNoticeService.saveBatch(commonBusinessNotices);
    }
    private void generateAppNotifyTraining(Set<Long> userIds,String msg,Long trainningId,CommonBean commonBean) {
        // app通知中心
        ArrayList<CommonBusinessNotice> commonBusinessNotices = new ArrayList<>();
        for (Long userId : userIds) {
            String msgBody = MessageFormat.format(msg, baseUserDao.getRealNameById(userId));
            CommonBusinessNotice appNotify = new CommonBusinessNotice();
            appNotify.setBusinessType(AppBusinessNoticeTypes.EDU.getType())
                    .setNoticeContent(msgBody)
                    .setNoticeUser(userId)
                    .setNoticeTitle(AppNotifyContentPre.TRAINNING_NOTIFY)
                    .setSendTime(new Date())
                    .setRefId(trainningId);
            appNotify.setOrgCode(commonBean.getOrgCode());
            appNotify.setTenentId(commonBean.getTenantId());
            commonBusinessNotices.add(appNotify);
        }
        commonBusinessNoticeService.saveBatch(commonBusinessNotices);
    }

}
